package com.wang.demo.application.client;

import com.wang.demo.application.handler.PersonChatHandler;
import com.wang.demo.protocol.codec.MessageCodecSharable;
import com.wang.demo.protocol.codec.ProcotoFrameDecoder;
import com.wang.demo.protocol.message.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author: Jeffrey
 * @date: 2022/01/21/10:26
 * @description:
 */
@Slf4j
public class ChatClient {

    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();
        Scanner scanner = new Scanner(System.in);

        MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();
        LoggingHandler LOGGING_HANDLER = new LoggingHandler();
        /*多线程倒计时锁 计数器*/
        CountDownLatch WAIT_FOR_LOGIN = new CountDownLatch(1);
        AtomicBoolean LOGIN = new AtomicBoolean(false);

        try {
            Channel channel = new Bootstrap()
                    .channel(NioSocketChannel.class)
                    .group(group)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new IdleStateHandler(0,3,0))
                                    .addLast(new ChannelDuplexHandler(){
                                        @Override
                                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                            IdleStateEvent stateEvent = (IdleStateEvent) evt;
                                            if (stateEvent.state() == IdleState.WRITER_IDLE){
                                                /*每3秒向服务器发送心跳包*/
                                                ctx.writeAndFlush(new PingMessage());
                                            }
                                        }
                                    })
                                    .addLast(new ProcotoFrameDecoder())
                                    .addLast(LOGGING_HANDLER)
                                    .addLast(MESSAGE_CODEC)
                                    .addLast(new ChannelInboundHandlerAdapter() {
                                        @Override
                                        public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {
                                            if (message instanceof LoginResponseMessage) {
                                                LoginResponseMessage loginResponseMessage = (LoginResponseMessage) message;
                                                if (loginResponseMessage.isSuccess()) {
                                                    /*登录成功*/
                                                    LOGIN.set(true);
                                                }
                                                /*计数-1 使得active继续执行,唤醒Sysin 线程*/
                                            }

                                            if (message instanceof ChatResponseMessage) {
                                                ChatResponseMessage msg = (ChatResponseMessage) message;
                                                if (!msg.isSuccess()){
                                                    System.out.println(msg.getReason());
                                                }else{
                                                    System.out.println(msg.getFrom() + "::" + msg.getContent());
                                                }
                                            }

                                            if (message instanceof GroupChatResponseMessage) {
                                                GroupChatResponseMessage msg = (GroupChatResponseMessage) message;
                                                System.out.println("[" + msg.getGroupName() + "]::" + msg.getFrom() + "::" + msg.getContent());
                                            }
                                            if (message instanceof GroupCreateResponseMessage){
                                                GroupCreateResponseMessage msg = (GroupCreateResponseMessage) message;
                                                if (msg.isSuccess()){
                                                    System.out.println(msg.getReason());
                                                }else{
                                                    System.out.println("创建失败，原因："+msg.getReason());
                                                }
                                            }

                                            if (message instanceof GroupJoinResponseMessage) {
                                                GroupJoinResponseMessage msg = (GroupJoinResponseMessage) message;
                                                System.out.println(msg.getReason());
                                            }

                                            if (message instanceof GroupMembersResponseMessage){
                                                GroupMembersResponseMessage msg = (GroupMembersResponseMessage) message;
                                                if (msg.getMembers() == null){
                                                    System.out.println("群聊不存在！");
                                                }else{
                                                    Set<String> members = msg.getMembers();
                                                    int i = 1;
                                                    for (String temp : members){
                                                        System.out.println("[User"+(i)+"]"+temp);
                                                        i++;
                                                    }
                                                }
                                            }

                                            if (message instanceof GroupQuitResponseMessage){
                                                GroupQuitResponseMessage msg = (GroupQuitResponseMessage) message;
                                                System.out.println(msg.getReason());
                                            }
                                            /*计数-1 使得active继续执行,唤醒Sysin 线程*/
                                            WAIT_FOR_LOGIN.countDown();
                                        }

                                        /**
                                         * 在连接建立后触发active 事件，发送一个LoginRequestMessage
                                         * 这里相当于是通过监听器（配置处理器）的方式进行
                                         * 监听active 事件，如果发生则发送
                                         * 也可以在连接建立后通过channel 进行发送
                                         * @param ctx 流水线上下文
                                         * @throws Exception e
                                         */
                                        @Override
                                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                            new Thread(new Runnable() {
                                                @Override
                                                public void run() {
                                                    /*1)创建LoginRequestMessage 发送服务器处理*/
                                                    System.out.println("username:");
                                                    String lin1 = scanner.nextLine();
                                                    System.out.println("password:");
                                                    String lin2 = scanner.nextLine();

                                                    /*构造消息对象*/
                                                    LoginRequestMessage message = new LoginRequestMessage(lin1, lin2);
                                                    /**
                                                     * 发送消息
                                                     * 这里会触发出站操作 ->MESSAGE_CODEC ->lOGGING_HANDLER
                                                     */
                                                    ctx.writeAndFlush(message);
                                                    try {
                                                        /*计数器等待
                                                         * 等待客户端的回复，计数+1 等待read唤醒此线程
                                                         * */
                                                        System.out.println("WAIT LOGIN---------");
                                                        WAIT_FOR_LOGIN.await();
                                                    } catch (InterruptedException e) {
                                                        e.printStackTrace();
                                                    }
                                                    /*线程被唤醒，线程通信 判断登录登录是否成功*/
                                                    if (!LOGIN.get()) {
                                                        /*channel 关闭会触发channelCloseFuture 然后会进行优雅关闭*/
                                                        System.out.println("LOGIN FAILURE! PLEASE CHECK YOUR username AND PASSWORD!");
                                                        ctx.channel().close();
                                                        return;
                                                    }
                                                    while (true) {
                                                        System.out.println("==================================");
                                                        /*发送消息*/
                                                        System.out.println("send [username] [content]");
                                                        /*发送群聊消息*/
                                                        System.out.println("gsend [group name] [content]");
                                                        /*创建群聊*/
                                                        System.out.println("gcreate [group name] [m1,m2,m3...]");
                                                        /*获取群成员*/
                                                        System.out.println("gmembers [group name]");
                                                        /*加群*/
                                                        System.out.println("gjoin [group name]");
                                                        /*退群*/
                                                        System.out.println("gquit [group name]");
                                                        /*退出*/
                                                        System.out.println("quit");
                                                        System.out.println("==================================");
                                                        String command = scanner.nextLine();
                                                        /*解析命令*/
                                                        String[] s = command.split(" ");
                                                        switch (s[0]) {
                                                            case "send":
                                                                ctx.writeAndFlush(new ChatRequestMessage(lin1, s[1], s[2]));
                                                                break;
                                                            case "gsend":
                                                                ctx.writeAndFlush(new GroupChatRequestMessage(lin1, s[1], s[2]));
                                                                break;
                                                            case "gcreate":
                                                                /*分组内的用户名*/
                                                                String[] split = s[2].split(",");
                                                                List<String> list = Arrays.asList(split);
                                                                Set<String> set = new HashSet<>(list);
                                                                /*加入自己*/
                                                                set.add(lin1);
                                                                ctx.writeAndFlush(new GroupCreateRequestMessage(s[1], set));
                                                                break;
                                                            case "gmembers":
                                                                ctx.writeAndFlush(new GroupMembersRequestMessage(s[1]));
                                                                break;
                                                            case "gjoin":
                                                                ctx.writeAndFlush(new GroupJoinRequestMessage(lin1, s[1]));
                                                                break;
                                                            case "gquit":
                                                                ctx.writeAndFlush(new GroupQuitRequestMessage(lin1, s[1]));
                                                                break;
                                                            case "quit":
                                                                /*正常退出会触发inActive 事件
                                                                 * 退出 正常退出和异常退出
                                                                 * */
                                                                ctx.channel().close();
                                                                return;
                                                        }
                                                    }
                                                }
                                            }, "Sysin").start();
                                        }
                                    })
                                    .addLast();
                        }
                    })
                    .connect("localhost", 8080)
                    .sync()
                    .channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }

    }
}
